package com.fight.strive.sys.modules.rbac.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fight.strive.sys.modules.rbac.constants.RbacRoleConstants;
import com.fight.strive.sys.modules.rbac.dao.RbacUserRoleMapper;
import com.fight.strive.sys.modules.rbac.dto.RbacUserRoleDto;
import com.fight.strive.sys.modules.rbac.entity.RbacRoleEntity;
import com.fight.strive.sys.modules.rbac.entity.RbacUserEntity;
import com.fight.strive.sys.modules.rbac.entity.RbacUserRoleEntity;
import com.fight.strive.sys.modules.rbac.service.RbacOrgRoleService;
import com.fight.strive.sys.modules.rbac.service.RbacRoleService;
import com.fight.strive.sys.modules.rbac.service.RbacUserRoleService;
import com.fight.strive.sys.modules.rbac.service.RbacUserService;
import com.fight.strive.sys.modules.sysadmin.utils.SysAdminUtils;
import com.fight.strive.sys.utils.CollectionUtils;
import com.fight.strive.sys.utils.ObjectUtils;
import com.fight.strive.sys.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * @author ZHOUXIANG
 */
@Service
@Slf4j
public class RbacUserRoleServiceImpl
        extends ServiceImpl<RbacUserRoleMapper, RbacUserRoleEntity>
        implements RbacUserRoleService {

    @Resource
    private RbacRoleService rbacRoleService;

    @Resource
    private RbacOrgRoleService rbacOrgRoleService;

    @Resource
    @Lazy
    private RbacUserService rbacUserService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(@Valid RbacUserRoleDto dto) {
        // 清除用户角色
        this.delete(dto);
        // 保存用户角色
        if (CollectionUtils.isNotEmpty(dto.getRoleIds())) {
            List<RbacUserRoleEntity> roles = new ArrayList<>();
            dto.getRoleIds().forEach(roleId -> {
                RbacRoleEntity roleEntity = rbacRoleService.getRoleById(roleId);
                dto.getUserIds().forEach(userId -> {
                    RbacUserRoleEntity userRoleEntity = new RbacUserRoleEntity();
                    userRoleEntity.setUserId(userId)
                            .setRoleId(roleEntity.getId())
                            .setRoleCode(roleEntity.getRoleCode())
                            .setRoleName(roleEntity.getRoleName());
                    this.saveOrUpdate(userRoleEntity);
                    roles.add(userRoleEntity);
                });
            });
            // 更新缓存
            dto.getUserIds().forEach(
                    userId -> this.updateRedisData(userId, roles));
        }
    }

    @Override
    public void delete(@Valid RbacUserRoleDto dto) {
        QueryWrapper<RbacUserRoleEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("user_id", dto.getUserIds());
        this.remove(queryWrapper);
        // 删除缓存
        dto.getUserIds().forEach(this::deleteRedisData);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<Long> getIds(Long userId) {
        List<Long> ids = (List<Long>) redisTemplate.opsForHash().get(
                RbacRoleConstants.USER_ROLE_IDS_KEY, userId);
        if (ObjectUtils.isNull(ids)) {
            ids = new ArrayList<>();
            List<RbacUserRoleEntity> list = this.getUserRoleListByUserId(userId);
            if (CollectionUtils.isNotEmpty(list)) {
                for (RbacUserRoleEntity entity:list) {
                    ids.add(entity.getRoleId());
                }
            }
            redisTemplate.opsForHash().put(
                    RbacRoleConstants.USER_ROLE_IDS_KEY, userId, ids);
        }
        return ids;
    }

    @Override
    @SuppressWarnings("unchecked")
    public HashSet<String> getUserRoles(Long userId) {
        if (ObjectUtils.isNull(userId)) {
            RbacUserEntity userEntity = SysAdminUtils.getCurrentUserEntity();
            if (ObjectUtils.notNull(userEntity)) {
                userId = userEntity.getId();
            }
        }
        HashSet<String> userRoleSet = null;
        if (ObjectUtils.notNull(userId)) {
            userRoleSet = (HashSet<String>) redisTemplate.opsForHash().get(
                    RbacRoleConstants.USER_ROLE_CODES_KEY, userId);
            if (CollectionUtils.isEmpty(userRoleSet)) {
                userRoleSet = new HashSet<>();
                if (CollectionUtils.isEmpty(userRoleSet)) {
                    List<RbacUserRoleEntity> userRoleList =
                            this.getUserRoleListByUserId(userId);
                    if (CollectionUtils.isNotEmpty(userRoleList)) {
                        for (RbacUserRoleEntity entity: userRoleList) {
                            userRoleSet.add(entity.getRoleCode());
                        }
                    }
                    Long orgId = rbacUserService.getUserOrgId(userId);
                    if (ObjectUtils.notNull(orgId)) {
                        HashSet<String> orgRoleSet =
                                rbacOrgRoleService.getOrgRoleCode(orgId);
                        userRoleSet.addAll(orgRoleSet);
                    }
                }
                // 更新缓存
                redisTemplate.opsForHash().put(
                        RbacRoleConstants.USER_ROLE_CODES_KEY,
                        userId, userRoleSet);
            }
        }
        return userRoleSet;
    }

    @Override
    public boolean userHasRole(Long userId, String roleCode) {
        HashSet<String> roleCodeSet = this.getUserRoles(userId);
        if (CollectionUtils.isNotEmpty(roleCodeSet)) {
            for (String s : roleCodeSet) {
                if (StringUtils.equalsIgnoreCase(s, roleCode)) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<RbacUserRoleEntity> getUserRoleListByUserId(Long userId) {
        List<RbacUserRoleEntity> userRoleList =
                (List<RbacUserRoleEntity>) redisTemplate.opsForHash().get(
                        RbacRoleConstants.USER_ROLE_LIST_KEY, userId);
        if (CollectionUtils.isEmpty(userRoleList)) {
            QueryWrapper<RbacUserRoleEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id", userId);
            userRoleList = this.list(queryWrapper);
            // 更新缓存
            this.updateRedisData(userId, userRoleList);
        }
        return userRoleList;
    }

    @Override
    public void updateRedisData(Long userId, List<RbacUserRoleEntity> roles) {
        // 存储 user role entity
        redisTemplate.opsForHash().put(
                RbacRoleConstants.USER_ROLE_LIST_KEY, userId, roles);
        HashSet<String> roleCodes = new HashSet<>();
        List<Long> roleIds = new ArrayList<>();
        for (RbacUserRoleEntity entity : roles) {
            roleCodes.add(entity.getRoleCode());
            roleIds.add(entity.getRoleId());
        }
        // 获取组织关联的角色
        Long orgId = rbacUserService.getUserOrgId(userId);
        if (ObjectUtils.notNull(orgId)) {
            // 如何有缓存就从缓存中取出
            HashSet<String> orgRoleSet =
                    rbacOrgRoleService.getOrgRoleCode(orgId);
            roleCodes.addAll(orgRoleSet);
        }
        // 存储 user role codes
        redisTemplate.opsForHash().put(
                RbacRoleConstants.USER_ROLE_CODES_KEY, userId, roleCodes);
        redisTemplate.opsForHash().put(
                RbacRoleConstants.USER_ROLE_IDS_KEY, userId, roleIds);
    }

    @Override
    public void deleteRedisData(Long userId) {
        redisTemplate.opsForHash().delete(
                RbacRoleConstants.USER_ROLE_LIST_KEY, userId);
        redisTemplate.opsForHash().delete(
                RbacRoleConstants.USER_ROLE_CODES_KEY, userId);
        redisTemplate.opsForHash().delete(
                RbacRoleConstants.USER_ROLE_IDS_KEY, userId);
    }
}
